﻿#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <sys/time.h>

#include "./hdtcodec.h"

#define VERSION 1
#define HEADER_PREFIX "HDT"
#define HEADER_LENGTH 10

#define RAND_STRING_DEFAULT_LENGTH 1024
static uint8_t rand_string[RAND_STRING_DEFAULT_LENGTH] = {0};
static inline void init_rand_string();
static inline uint8_t hex_str_to_hex(const char* hex_str); // 2bytes -> 1byte

#define HDT_CODEC_CODE 1

// eg: 0xf0, 'real value' high 4bits mixed 'rand value' low 4bits, AND 'real value' low 4bits mixed 'rand value' high 4bits
static const uint8_t RAND_MIX_BITS_NUM[] = {0xfe, 0xfc, 0xf8, 0xe0, 0xc0, 0x80};
int hdt_decode_v0(const char* encoded_src, char** decoded_dst)
{
	init_rand_string();
	int ret = -1;
        int i;

	if(encoded_src == NULL)
		return ret;
	
	/** Valid 'encoded_src' length. **/
	int src_len = strlen(encoded_src);
	if(src_len <= (HEADER_LENGTH<<1) )
		return ret;
	
	/** parse header from 'encoded_src' **/
	const char* src = encoded_src;
	// HDT, 'H',"48"; 'D',"44"; 'T',"54" 
	if( !( (src[0] == '4' && src[1] == '8') && 
		(src[2] == '4' && src[3] == '4') &&
		(src[4] == '5' && src[5] == '4') ) )
		return ret;
		
	src += 6;
	/** parse version, codec code, content length **/
	// version, 1byte
	uint8_t version = hex_str_to_hex(src);
	src += 2;
	// codec code, 1byte
	uint8_t codec_code = hex_str_to_hex(src);
	src += 2;
	// codec param, 1byte
	uint8_t codec_param = hex_str_to_hex(src);
	if(codec_param >= sizeof(RAND_MIX_BITS_NUM) )
		return ret;
	src += 2;
	// content length, 4bytes
	uint32_t length = (hex_str_to_hex(src)<<24) | (hex_str_to_hex(src+2)<<16) | (hex_str_to_hex(src+4)<<8) | hex_str_to_hex(src+6);
	src += 8;
	
	// Valid content length.
	if(src_len < ( (HEADER_LENGTH+length)<<1) )
		return ret;
	
	/** Decoded content according to codec code. **/
	switch(codec_code) {
		case 1:
		default:
		{
			uint32_t dst_len = length >> 1, rand_pos = 0;
			uint8_t e1 = 0, e2 = 0, d = 0;
			uint8_t mixed_bit1 = RAND_MIX_BITS_NUM[codec_param];
			uint8_t mixed_bit2 = 0xff ^ mixed_bit1;
			*decoded_dst = (char*) malloc(dst_len+1);
			if(*decoded_dst == NULL)
				return ret;
			for(i=0; i<dst_len; ++i)
			{
				rand_pos = i % RAND_STRING_DEFAULT_LENGTH;
				e1 = hex_str_to_hex(src);
				e2 = hex_str_to_hex(src+2);
				d = (e1 & mixed_bit1) | (e2 & mixed_bit2);
				(*decoded_dst)[i] = d ^ rand_string[rand_pos];
				src += 4;
			}
			
			(*decoded_dst)[dst_len] = 0;
			ret = dst_len;
			break;
		}
	}
	
	return ret;
}

static const char HEX_CHARS_TABLE[] = "0123456789ABCDEF";
int hdt_encode_v0(const char* src, char** encoded_dst)
{
	int ret = -1;
        int i = 0;
        int size_t = 0;
	// write encoded buf header
	int pos = 0;
        int dst_pos = 0;
	uint8_t mixed_bit_param = 0;

	init_rand_string();


	if(src == NULL)
		return ret; 
	
	int len = strlen(src);
	int buf_len = (len<<1);
	int dst_len = HEADER_LENGTH + buf_len;
	uint8_t encoded_header[HEADER_LENGTH] = {0};
	*encoded_dst = (char*) malloc((dst_len << 1) + 1);
	if(*encoded_dst == NULL)
	{
		if(*encoded_dst)
			free(*encoded_dst);
		return ret;
	}
	
	/** Setting rand seed. **/
	struct timeval tv = {0, 0};
	gettimeofday(&tv, NULL);
	srand(tv.tv_sec*1000000 + tv.tv_usec);
	

	// prefix: 'HDT', 3bytes
	memcpy(encoded_header, HEADER_PREFIX, 3);
	pos += 3;
	// version: 1, 1byte
	encoded_header[pos++] = VERSION;
	// codec code: 1, 1byte
	encoded_header[pos++] = HDT_CODEC_CODE;
	// codec param: (0~5) 1byte
	mixed_bit_param = rand() % sizeof(RAND_MIX_BITS_NUM);
	encoded_header[pos++] = mixed_bit_param;
	// buf length, 4bytes
	encoded_header[pos++] = buf_len >> 24;
	encoded_header[pos++] = (buf_len >> 16) & 0xff;
	encoded_header[pos++] = (buf_len >> 8) & 0xff;
	encoded_header[pos++] = buf_len & 0xff;
	/** convert header to HEX string and write in encoded_dst **/
	uint8_t chr = 0;
	for(i=0; i<HEADER_LENGTH; ++i)
	{
		chr = encoded_header[i];
		(*encoded_dst)[dst_pos++] = HEX_CHARS_TABLE[ chr >> 4 ];
		(*encoded_dst)[dst_pos++] = HEX_CHARS_TABLE[ chr & 0x0f ];
	}
	
	int rand_pos = 0;
	uint8_t tmp = 0, rand_n = 0;
	uint8_t mixed_bit1 = RAND_MIX_BITS_NUM[mixed_bit_param];
	uint8_t mixed_bit2 = 0xff ^ mixed_bit1;
	uint8_t e1 = 0, e2 = 0;
	for(i=0; i<len; ++i)
	{
		rand_pos = i % RAND_STRING_DEFAULT_LENGTH;
		tmp = src[i] ^ rand_string[rand_pos];
		rand_n = rand() % 0xff;
		e1 = (tmp & mixed_bit1) | (rand_n & mixed_bit2); // eg: 'tmp' first 5 bit, 'rand_n' last 3 bit.
		e2 = (tmp & mixed_bit2) | (rand_n & mixed_bit1); // eg: 'tmp' last 3 bit, 'rand_n' first 5 bit.
		
		// convert to HEX, for encoded_dst
		(*encoded_dst)[dst_pos++] = HEX_CHARS_TABLE[ e1 >> 4 ];
		(*encoded_dst)[dst_pos++] = HEX_CHARS_TABLE[ e1 & 0x0f ];

		(*encoded_dst)[dst_pos++] = HEX_CHARS_TABLE[ e2 >> 4 ];
		(*encoded_dst)[dst_pos++] = HEX_CHARS_TABLE[ e2 & 0x0f ];
	}
	(*encoded_dst)[dst_pos] = 0;
	
	ret = dst_pos;
	return ret;
}

void hdt_release(void* ptr)
{
	if(ptr)
	{
		free(ptr);
		ptr = NULL;
	}
}

static inline void init_rand_string()
{
	if(rand_string[0] == 0x0 && rand_string[1] == 0x0)
		memcpy(rand_string, "\x26\x45\x49\x8e\xb9\x43\xcd\x9b\xfa\x0c\xf3\x32\x7a\x1a\x89\x6f\x14\x14\xc0\x91\x42\x10\xeb\x97\x61\x30\x42\xc8\x23\x94\x97\x50\x4b\xe3\x4d\x48\x97\x7a\xd8\x66\xd0\xf8\xad\x25\xe8\xb8\xe3\x01\xb7\xec\xea\x4f\x27\xf5\xa3\xae\x59\x24\x24\x8b\xb8\x6f\x99\x52\xe8\xf4\x70\x3d\xf3\x25\x48\xd0\x5d\x80\xd7\x84\xc5\x5a\x38\x9a\x62\x5c\x59\x2c\xef\x6a\xfe\x7a\xda\x87\x85\xe4\xd2\x63\xe4\xbf\x72\xb4\x1d\x50\xc5\xfb\x8b\xb0\x40\xe3\xcd\x62\x84\x4f\xb5\x4d\xfa\xc9\x77\x84\x28\xf0\x0e\x67\xb8\x07\x01\x1f\xb2\xca\x0a\x04\xc4\xb4\xc0\x5a\x27\xcf\xd3\x8e\xfd\xb8\xee\x40\x36\xfb\xcf\xe2\xce\xee\xb4\x2a\x7a\x23\x25\x1c\xa8\xa3\xb8\xf8\x3d\x13\xdd\x3f\x9d\x5b\xcc\xa5\xd6\x04\x97\xa7\x7d\xe8\x9e\x1a\x15\xd7\x3d\xd1\x0d\x5a\x84\xe1\xb8\xad\xdc\x6f\xc2\x2f\x51\x24\x7e\x3e\x7c\x47\x9e\x5e\x36\x6f\xb8\x0e\x91\xed\x0a\xb1\x41\xd7\xfb\xf0\x7a\xcb\x4b\x70\xdf\xe3\xd7\xd3\x0f\x6b\x85\x51\xf1\xc5\xa2\xe3\xbf\xa3\xcf\x4b\x78\xec\xd2\x7c\x3e\x08\x53\x25\x43\x5c\x6a\xe5\xc4\x32\x01\xe5\x40\x5d\xef\x68\x5f\xd0\x52\x08\x49\xf9\x7b\x2d\xc9\x11\x9a\x84\x0f\xc3\x50\x6b\x61\xcd\xec\x29\x93\xf9\x04\xeb\xa0\x9b\x8b\x47\x26\x70\xbe\xd6\x2f\x14\xec\xa1\x06\xcc\x80\x59\x80\x80\x4c\xbc\xfd\x8f\x80\x8f\x80\xce\xcc\x78\x23\x7a\x12\xc9\x54\xd1\x2a\x72\x96\xb1\xa4\x1b\xa8\xd9\x01\x41\xdd\xc0\xca\x3a\x0c\x96\x99\x7e\x49\xdf\xd5\x30\xe5\xca\xcf\x9f\xe8\x98\x7f\xdd\xeb\x54\x83\x19\x17\x2a\xa7\xc6\xb5\x00\x1b\x5f\x09\x4b\x97\x35\x5b\xaa\x97\xa5\x7a\xb6\xe6\x29\x1f\xb7\x34\x00\xe0\x04\xdc\x17\x35\x56\x1d\xc4\x34\x56\xee\x00\x88\x95\xac\x48\x45\xd8\xc9\x45\x9c\xc9\xc0\x63\x9f\x8c\x70\xe7\x80\xbe\x9f\x51\x09\xe0\x5d\x7a\xc4\xbb\x65\x4e\x7e\x27\x48\xfd\x14\x5e\x8a\x1a\x51\xc4\xe7\x7a\x47\xb5\xf3\xbc\xcb\x4a\xce\xf0\x1d\x3e\x96\x1b\x63\xc8\x5a\x9f\xf1\xa1\x2b\xe4\x09\x02\xa5\x10\x11\x51\x6e\x4b\xf3\x63\x99\x73\x14\x61\xf2\x09\x12\x50\x07\x5b\xd0\x47\x86\x3c\xf7\x46\x4d\xb3\x56\xd6\x25\x0b\xea\xc0\x99\x96\xfa\x1a\xf5\xa1\xf4\xf7\x2e\xe9\x01\x76\xe3\x77\x0e\x72\x3e\xd2\x3a\x9f\xbd\xe5\xd0\x5a\xd7\xf6\x51\xda\x68\x98\xbf\x2f\x5f\x07\x13\x18\x22\x39\x20\xbe\x17\xa6\x4c\x51\x52\x86\x5c\x75\xc0\x17\xb9\xc0\x70\x9f\x7a\x77\xbc\xbb\x08\xe6\xf4\x36\xa7\x8e\xd1\xea\x5d\x5f\xd8\x82\x47\x65\xeb\x07\x7e\x81\xab\x0c\x1f\x96\xfb\xf9\x77\xd6\x36\x41\x8b\x07\xe5\x6d\xde\x65\x29\xe4\xc6\x93\x73\x5e\x21\xb6\xe2\xc0\xbe\x0d\x63\x7e\x5e\xcb\xc2\xaf\x7b\x84\x84\x63\x5d\xc6\x89\xf8\x97\x65\x5d\x06\x04\xf2\xc8\xf5\xc3\xe8\xe8\x14\xea\x1a\x6f\x0c\xde\xcc\xde\x93\xfa\x2a\xce\x70\x1c\xdf\x11\x4e\x97\xa2\x49\xc9\x3b\xe0\xd3\x16\xe3\xe3\xfd\x91\x4b\x2b\x7a\x50\x0b\xb9\x0d\xce\x04\xae\x5d\x60\xa3\x47\xe8\x1d\x2e\xc2\x7b\xca\x99\xb5\xba\x08\x93\x3f\x9d\xc1\x32\xbd\xd4\xb1\x8b\x69\x94\x6d\x95\xf7\xb2\x4a\x10\x2f\x86\x92\x6d\x84\x10\x5b\xe2\xb6\x43\xfb\x1a\xfc\x04\xde\x44\x38\xb0\xc2\x67\x3c\xbc\x25\xb9\x2a\xc9\x58\xe1\xed\xba\x92\x67\xc1\x24\x6c\xb4\xf6\x26\x24\x6f\x3b\x0b\x19\x13\x2b\x31\xc9\xf2\xf5\x2b\xe3\x3e\xcc\x0b\x0e\x8c\x66\xcd\x92\x8d\x64\x60\x8e\x71\xcc\xa2\xc4\x13\x66\x72\xc3\x10\x43\x99\xeb\x41\x24\xb0\x9c\xcb\x1b\xcb\x85\x35\x12\x77\xe3\x40\x0c\xf9\xaf\x78\xdc\x47\xf8\x1b\xa8\x16\x85\x6a\x4f\x1e\xdb\x00\x35\x5a\xd5\xa8\x81\x58\x7c\x7b\x3a\xf6\x1d\x8e\x62\x9f\xf1\x94\x79\xca\x5a\x46\x0b\xf3\xe4\xd6\xa4\x33\xad\x61\xe2\x39\x5f\x80\x6c\xdb\x49\x8e\x91\xdb\xe3\xb6\xdb\x33\x32\xd4\x49\x02\x56\x4c\x07\xe2\x50\x5e\xd5\x4f\xb2\x17\x82\xd3\x4f\x5f\x05\x35\x18\x59\xb8\x51\x4d\xfc\xd5\x59\x9f\xb3\xfb\xae\x60\xce\x84\x84\x03\x30\x7a\x84\xe1\xee\x59\x26\x1c\x56\xf8\xf5\x0f\x93\xcd\x68\xa3\x52\x5c\x91\x7e\xc2\x83\xce\x51\xab\x0a\x39\xd9\x8f\x86\x6e\x41\x8a\x93\x99\x74\x65\xe3\x2b\x2c\x10\xaf\xbb\x29\x6c\x8a\x8d\xf1\x3a\x0e\x18\x89\x71\x04\xa1\xef\xdd\x6a\xc4\xe0\xe7\xf6\xed\x29\x2c\x02\x30\x82\x79\x7a\xf3\x43\xc4\x48\x6a\xeb\x21\x3c\xc1\x48\x62\xf9\x02\xb4\xde\xf5\x16\x44\x0f\xf5\xe9\x4e\x6f\x2f\x91\x2b\xe7\x6a\x77\x97\xa0\xd2\xf8\x14\x79\xd1\xec\xb7\xaa\x16\x89\x3e\xa0\x3e\x14\xd4\xa6\xa9\xda\x6c\x83\x77\x64\x8f\x81\x3d\x4f\x89\x96\x67\xc5\xd5\x17\x5b\x18\xa7\xb7\x53\x51\x84\x46\x82\x4b\xc1\x4e\xed\xb3\xd6\xa4\xa4\xfc\xd8\x85\x30\x15\xa2\xbd\xe5\x7d\xda\x13\x84\x12\xf2\xdd\x30\x92\xd8\x0b\x47\xc7\xdc\x73\x9c", RAND_STRING_DEFAULT_LENGTH);
}

static inline uint8_t hex_str_to_hex(const char* hex_str) // 2bytes -> 1byte
{
	uint8_t uchr = 0x00;
	if(hex_str)
	{
		uint8_t high = hex_str[0] < 'A'? /*'0'~'9'*/hex_str[0] - '0': /*'A'~'F'*/hex_str[0] - 'A' + 10;
		uint8_t low = hex_str[1] < 'A'? /*'0'~'9'*/hex_str[1] - '0': /*'A'~'F'*/hex_str[1] - 'A' + 10;
		uchr = ( (high<<4) & 0xf0) | (low & 0x0f);
	}
	
	return uchr;
}
